diff --git a/internal/download/dev_portal.go b/internal/download/dev_portal.go index 60e38013c..00893f6b7 100644 --- a/internal/download/dev_portal.go +++ b/internal/download/dev_portal.go @@ -5,8 +5,10 @@ package download import ( "bytes" "context" + "crypto" "crypto/sha1" "crypto/tls" + "encoding/base64" "encoding/binary" "encoding/json" "fmt" @@ -30,6 +32,7 @@ import ( "github.com/AlecAivazis/survey/v2/terminal" "github.com/PuerkitoBio/goquery" "github.com/apex/log" + "github.com/blacktop/ipsw/internal/srp" "github.com/blacktop/ipsw/internal/utils" "github.com/pkg/errors" ) @@ -53,7 +56,10 @@ const ( listDownloadsActionURL = "https://developer.apple.com/services-account/QH65B2/downloadws/listDownloads.action" adcDownloadURL = "https://developerservices2.apple.com/services/download?path=" - loginURL = "https://idmsa.apple.com/appleauth/auth/signin" + authURL = "https://idmsa.apple.com/appleauth/auth" + loginURL = authURL + "/signin" + initURL = loginURL + "/init" + completeURL = loginURL + "/complete?isRememberMeEnabled=false" trustURL = "https://idmsa.apple.com/appleauth/auth/2sv/trust" itcServiceKey = "https://appstoreconnect.apple.com/olympus/v1/app/config?hostname=itunesconnect.apple.com" @@ -174,8 +180,13 @@ type authService struct { type auth struct { AccountName string `json:"accountName,omitempty"` Password string `json:"password,omitempty"` - RememberMe bool `json:"rememberMe,omitempty"` + RememberMe bool `json:"rememberMe"` TrustTokens []string `json:"trust_tokens,omitempty"` + A string `json:"a,omitempty"` + Protocols []string `json:"protocols,omitempty"` + M1 string `json:"m1,omitempty"` + C string `json:"c,omitempty"` + M2 string `json:"m2,omitempty"` } type trustedPhoneNumber struct { @@ -561,6 +572,138 @@ func (dp *DevPortal) getHashcachHeaders() error { return nil } +type srpInitResponse struct { + Iteration int `json:"iteration,omitempty"` + Salt string `json:"salt,omitempty"` + Protocol string `json:"protocol,omitempty"` + B string `json:"b,omitempty"` + C string `json:"c,omitempty"` +} + +type srpCompleteResponse struct { + AccountName string `json:"accountName,omitempty"` + RememberMe bool `json:"rememberMe,omitempty"` + Skip2Fa bool `json:"skip2FA,omitempty"` + Pause2Fa bool `json:"pause2FA,omitempty"` + DomainAutoFill bool `json:"domainAutoFill,omitempty"` + M1 string `json:"m1,omitempty"` + C string `json:"c,omitempty"` + M2 string `json:"m2,omitempty"` + ParseRememberMeTokens bool `json:"parseRememberMeTokens,omitempty"` + ServiceErrors []serviceError `json:"serviceErrors,omitempty"` +} + +func (dp *DevPortal) generateSRP(username, password string) (*http.Response, error) { + s, err := srp.NewWithHash(crypto.SHA256, 2048) + if err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + + json.NewEncoder(buf).Encode(&auth{ + AccountName: username, + A: base64.StdEncoding.EncodeToString(s.A.Bytes()), + Protocols: []string{"s2k", "s2k_fo"}, + }) + + req, err := http.NewRequest("POST", initURL, buf) + if err != nil { + return nil, fmt.Errorf("failed to create http POST request: %v", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Requested-With", "XMLHttpRequest") + req.Header.Set("X-Apple-Widget-Key", dp.config.WidgetKey) + req.Header.Set(hashcashHeader, dp.config.HashCash) + req.Header.Add("User-Agent", userAgent) + req.Header.Set("Accept", "application/json, text/javascript") + + initResponse, err := dp.Client.Do(req) + if err != nil { + return nil, err + } + defer initResponse.Body.Close() + + body, err := io.ReadAll(initResponse.Body) + if err != nil { + return nil, err + } + + log.Debugf("SRP INIT: (%d):\n%s\n", initResponse.StatusCode, string(body)) + + var srpInit srpInitResponse + if err := json.Unmarshal(body, &srpInit); err != nil { + return nil, fmt.Errorf("failed to deserialize response body JSON: %v", err) + } + + saltBytes, err := base64.StdEncoding.DecodeString(srpInit.Salt) + if err != nil { + return nil, err + } + + bBytes, err := base64.StdEncoding.DecodeString(srpInit.B) + if err != nil { + return nil, err + } + + cli, err := s.NewClient([]byte(username), []byte(password), saltBytes, srpInit.Iteration) + if err != nil { + return nil, err + } + + m1, m2, err := cli.Generate(saltBytes, bBytes) + if err != nil { + return nil, err + } + + buf.Reset() + + json.NewEncoder(buf).Encode(&auth{ + AccountName: username, + C: srpInit.C, + M1: base64.StdEncoding.EncodeToString(m1), + M2: base64.StdEncoding.EncodeToString(m2), + RememberMe: false, + }) + + req, err = http.NewRequest("POST", completeURL, buf) + if err != nil { + return nil, fmt.Errorf("failed to create http POST request: %v", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Requested-With", "XMLHttpRequest") + req.Header.Set("X-Apple-Widget-Key", dp.config.WidgetKey) + req.Header.Set(hashcashHeader, dp.config.HashCash) + req.Header.Add("User-Agent", userAgent) + req.Header.Set("Accept", "application/json, text/javascript") + + completeResponse, err := dp.Client.Do(req) + if err != nil { + return nil, err + } + defer completeResponse.Body.Close() + + body, err = io.ReadAll(completeResponse.Body) + if err != nil { + return nil, err + } + + log.Debugf("SRP COMPLETE: (%d):\n%s\n", completeResponse.StatusCode, string(body)) + + var srpComp srpCompleteResponse + if err := json.Unmarshal(body, &srpComp); err != nil { + return nil, fmt.Errorf("failed to deserialize response body JSON: %v", err) + } + + if len(srpComp.ServiceErrors) > 0 { + return nil, fmt.Errorf("failed to complete SRP: %s", srpComp.ServiceErrors[0].Message) + } + + return completeResponse, nil +} + func (dp *DevPortal) signIn(username, password string) error { if err := dp.getHashcachHeaders(); err != nil { @@ -600,6 +743,13 @@ func (dp *DevPortal) signIn(username, password string) error { log.Debugf("POST Login: (%d):\n%s\n", response.StatusCode, string(body)) + if response.StatusCode == 503 { // try NEW SRP login + response, err = dp.generateSRP(username, password) + if err != nil { + return err + } + } + if response.StatusCode == 409 { dp.xAppleIDAccountCountry = response.Header.Get("X-Apple-Id-Account-Country") dp.config.SessionID = response.Header.Get("X-Apple-Id-Session-Id") diff --git a/internal/srp/prime.go b/internal/srp/prime.go new file mode 100644 index 000000000..4f8f29bfc --- /dev/null +++ b/internal/srp/prime.go @@ -0,0 +1,92 @@ +// prime.go - Generate safe primes +// +// Copyright 2013-2017 Sudhi Herle <sudhi.herle-at-gmail-dot-com> +// License: MIT + +package srp + +/* UPDATED TO WORK WITH APPLE'S SRP IMPLEMENTATION by blacktop */ + +import ( + "crypto/rand" + "math/big" +) + +// safePrime generates a safe prime; i.e., a prime 'p' such that 2p+1 is also prime. +func safePrime(bits int) (*big.Int, error) { + + a := new(big.Int) + for { + p, err := rand.Prime(rand.Reader, bits) + if err != nil { + return nil, err + } + + // 2p+1 + a = a.Lsh(p, 1) + a = a.Add(a, one) + if a.ProbablyPrime(20) { + return a, nil + } + } + + // never reached + return nil, nil +} + +// Return true if g is a generator for safe prime p +// +// From Cryptography Theory & Practive, Stinson and Paterson (Th. 6.8 pp 196): +// +// If p > 2 is a prime and g is in Zp*, then +// g is a primitive element modulo p iff g ^ (p-1)/q != 1 (mod p) +// for all primes q such that q divides (p-1). +// +// "Primitive Element" and "Generator" are the same thing in Number Theory. +// +// Code below added as a result of bug pointed out by Dharmalingam G. (May 2019) +func isGenerator(g, p *big.Int) bool { + p1 := big.NewInt(0).Sub(p, one) + q := big.NewInt(0).Rsh(p1, 1) // q = p-1/2 = ((p-1) >> 1) + + // p is a safe prime. i.e., it is of the form 2q+1 where q is prime. + // + // => p-1 = 2q, where q is a prime. + // + // All factors of p-1 are: {2, q, 2q} + // + // So, our check really comes down to: + // 1) g ^ (p-1/2q) != 1 mod p + // => g ^ (2q/2q) != 1 mod p + // => g != 1 mod p + // Trivial case. We ignore this. + // + // 2) g ^ (p-1/2) != 1 mod p + // => g ^ (2q/2) != 1 mod p + // => g ^ q != 1 mod p + // + // 3) g ^ (p-1/q) != 1 mod p + // => g ^ (2q/q) != 1 mod p + // => g ^ 2 != 1 mod p + // + + // g ^ 2 mod p + if !ok(g, big.NewInt(0).Lsh(one, 1), p) { + return false + } + + // g ^ q mod p + if !ok(g, q, p) { + return false + } + + return true +} + +func ok(g, x *big.Int, p *big.Int) bool { + z := big.NewInt(0).Exp(g, x, p) + // the expmod should NOT be 1 + return z.Cmp(one) != 0 +} + +// vim: noexpandtab:sw=8:ts=8:tw=92: diff --git a/internal/srp/srp.go b/internal/srp/srp.go new file mode 100644 index 000000000..e197f07ca --- /dev/null +++ b/internal/srp/srp.go @@ -0,0 +1,651 @@ +// srp.go - golang implementation of SRP-6a +// +// Copyright 2013-2017 Sudhi Herle <sudhi.herle-at-gmail-dot-com> +// License: MIT +// + +// Package srp implements SRP-6a per [1]. It uses the standard library +// and the golang extended library and nothing else. +// +// This implementation is accurate as of Aug 2012 edition of the SRP +// specification [1]. +// +// To verify that the client has generated the same key "K", the client sends +// "M" -- a hash of all the data it has and it received from the server. To +// validate that the server also has the same value, it requires the server to send +// its own proof. In the SRP paper [1], the authors use: +// +// M = H(H(N) xor H(g), H(I), s, A, B, K) +// M' = H(A, M, K) +// +// We use a simpler construction: +// +// M = H(K, A, B, I, s, N, g) +// M' = H(M, K) +// +// In this implementation: +// +// H = BLAKE2() +// k = H(N, g) +// x = H(s, I, P) +// I = anonymized form of user identity (BLAKE2 of value sent by client) +// P = hashed password (expands short passwords) +// +// Per RFC-5054, we adopt the following padding convention: +// +// k = H(N, pad(g)) +// u = H(pad(A), pad(B)) +// +// References: +// +// [1] http://srp.stanford.edu/design.html +// [2] http://srp.stanford.edu/ +package srp + +// Implementation Notes +// --------------------- +// +// Conventions +// N A large safe prime (N = 2q+1, where q is prime) +// All arithmetic is done modulo N. +// g A generator modulo N +// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) +// s User's salt +// I Username +// p Cleartext Password +// H() One-way hash function +// ^ (Modular) Exponentiation +// u Random scrambling parameter +// a,b Secret ephemeral values +// A,B Public ephemeral values +// x Private key (derived from p and s) +// v Password verifier +// +// The host stores passwords using the following formula: +// +// s = randomsalt() (same length as N) +// I = H(I) +// p = H(p) (hash/expand I & p) +// t = H(I, ":", p) +// x = H(s, t) +// v = g^x (computes password verifier) +// +// The host then keeps {I, s, v} in its password database. +// +// The authentication protocol itself goes as follows: +// +// Client Server +// -------------- ---------------- +// I, p = < user input > +// I = H(I) +// p = H(p) +// a = random() +// A = g^a % N +// I, A --> +// s, v = lookup(I) +// b = random() +// B = (kv + g^b) % N +// u = H(A, B) +// S = ((A * v^u) ^ b) % N +// K = H(S) +// M' = H(K, A, B, I, s, N, g) +// <-- s, B +// u = H(A, B) +// x = H(s, p) +// S = ((B - k (g^x)) ^ (a + ux)) % N +// K = H(S) +// M = H(K, A, B, I, s, N, g) +// +// M --> +// M must be equal to M' +// Z = H(M, K) +// <-- Z +// Z' = H(M, K) +// Z' must equal Z +// ----------------------------------------------------------------- +// When the server receives <I, A>, it can compute everything: shared key +// and proof-of-generation (M'). The shared key is "K". +// +// To verify that the client has generated the same key "K", the client sends +// "M" -- a hash of all the data it has and it received from the server. To +// validate that the server also has the same value, it requires the server to send +// its own proof. We use a simpler construction: +// +// M = H(K, A, B, I, s, N, g) +// M' = H(M, K) +// +// Client & Server also employ the following safeguards: +// +// 1. The user will abort if he receives B == 0 (mod N) or u == 0. +// 2. The host will abort if it detects that A == 0 (mod N). +// 3. The user must show his proof of K first. If the server detects that the +// user's proof is incorrect, it must abort without showing its own proof of K. + +/* UPDATED TO WORK WITH APPLE'S SRP IMPLEMENTATION by blacktop */ + +import ( + "bytes" + "crypto" + CR "crypto/rand" + "crypto/sha256" + "crypto/subtle" + "encoding/hex" + "fmt" + "io" + "math/big" + "strconv" + "strings" + + // stdlib has an enum for Blake2b_256; this lib registers itself against it. + + _ "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/pbkdf2" +) + +// SRP represents an environment for the client and server to share certain properties; +// notably the hash function and prime-field size. The default hash function is +// Blake2b-256. Any valid hash function as documented in "crypto" can be used. +// There are two ways for creating an SRP environment: +// +// New() +// NewWithHash() +type SRP struct { + h crypto.Hash + pf *primeField + a *big.Int + A *big.Int +} + +// FieldSize returns this instance's prime-field size in bits +func (s *SRP) FieldSize() int { + return s.pf.n * 8 +} + +// New creates a new SRP environment using a 'bits' sized prime-field for +// use by SRP clients and Servers.The default hash function is Blake-2b-256. +func New(bits int) (*SRP, error) { + return NewWithHash(crypto.BLAKE2b_256, bits) +} + +// NewWithHash creates a new SRP environment using the hash function 'h' and +// 'bits' sized prime-field size. +func NewWithHash(h crypto.Hash, bits int) (*SRP, error) { + + pf, err := findPrimeField(bits) + if err != nil { + return nil, err + } + + s := &SRP{ + h: h, + pf: pf, + a: randBigInt(pf.n * 8), + } + + s.A = big.NewInt(0).Exp(pf.g, s.a, pf.N) + + return s, nil +} + +// ServerBegin processes the first message from an SRP client and returns a decoded +// identity string and client public key. The caller is expected to use the identity +// to lookup durable storage and find the corresponding encoded Verifier. This verifier +// is given to MakeSRPVerifier() to create an instance of SRP and Verifier. +func ServerBegin(creds string) (string, *big.Int, error) { + v := strings.Split(creds, ":") + if len(v) != 2 { + return "", nil, fmt.Errorf("srp: invalid client public key") + } + + //fmt.Printf("v0: %s\nv1: %s\n", v[0], v[1]) + + A, ok := big.NewInt(0).SetString(v[1], 16) + if !ok { + return "", nil, fmt.Errorf("srp: invalid client public key A") + } + + return v[0], A, nil +} + +// Verifier represents password verifier that resides on an SRP server. +type Verifier struct { + i []byte // hashed identity + s []byte // random salt (same size as prime field) + v []byte // password verifier + h crypto.Hash // hash algo used for building v + pf *primeField // the prime field (g, N) +} + +// Verifier generates a password verifier for user I and passphrase p +// in the environment 's'. It returns an instance of Verifier that holds the +// parameters needed for a future authentication. +func (s *SRP) Verifier(I, p []byte) (*Verifier, error) { + ih := s.hashbyte(I) + ph := s.hashbyte(p) + pf := s.pf + salt := randbytes(pf.n) + x := s.hashint(ih, ph, salt) + r := big.NewInt(0).Exp(pf.g, x, pf.N) + + v := &Verifier{ + i: ih, + s: salt, + v: r.Bytes(), + h: s.h, + pf: pf, + } + + return v, nil +} + +// MakeSRPVerifier decodes the encoded verifier into an SRP environment +// and Verifier instance. 'b' is an encoded verifier string previously +// returned by Verifier.Encode(). A caller of this function uses the identity +// provided by the SRP Client to lookup some DB to find the corresponding encoded +// verifier string; this encoded data contains enough information to create a +// valid SRP instance and Verifier instance. +func MakeSRPVerifier(b string) (*SRP, *Verifier, error) { + v := strings.Split(b, ":") + if len(v) != 7 { + return nil, nil, fmt.Errorf("verifier: malformed fields exp 5, saw %d", len(v)) + } + + ss := v[0] + sz, err := strconv.Atoi(ss) + if err != nil || sz <= 0 { + return nil, nil, fmt.Errorf("verifier: malformed field size %s", ss) + } + + ss = v[1] + p, ok := big.NewInt(0).SetString(ss, 16) + if !ok { + return nil, nil, fmt.Errorf("verifier: malformed prime %s", ss) + } + + ss = v[2] + g, ok := big.NewInt(0).SetString(ss, 16) + if !ok { + return nil, nil, fmt.Errorf("verifier: malformed generator %s", ss) + } + + ss = v[3] + h, err := strconv.Atoi(ss) + if err != nil || h <= 0 { + return nil, nil, fmt.Errorf("verifier: malformed hash type %s", ss) + } + + hf := crypto.Hash(h) + if !hf.Available() { + return nil, nil, fmt.Errorf("verifier: hash algorithm %d unavailable", h) + } + + ss = v[4] + i, err := hex.DecodeString(ss) + if err != nil { + return nil, nil, fmt.Errorf("verifier: invalid identity: %s", ss) + } + + ss = v[5] + s, err := hex.DecodeString(ss) + if err != nil { + return nil, nil, fmt.Errorf("verifier: invalid salt: %s", ss) + } + + ss = v[6] + vx, err := hex.DecodeString(ss) + if err != nil { + return nil, nil, fmt.Errorf("verifier: invalid verifier: %s", ss) + } + + sr := &SRP{ + h: hf, + pf: &primeField{ + n: sz, + N: p, + g: g, + }, + } + + vf := &Verifier{ + i: i, + s: s, + v: vx, + h: hf, + pf: sr.pf, + } + + return sr, vf, nil +} + +// Encode the verifier into a portable format - returns a tuple +// <Identity, Verifier> as portable strings. The caller can store +// the Verifier against the Identity in non-volatile storage. +// An SRP client will supply Identity and its public key - whereupon, +// an SRP server will use the Identity as a key to lookup +// the rest of the encoded verifier data. +func (v *Verifier) Encode() (string, string) { + var b bytes.Buffer + + ih := hex.EncodeToString(v.i) + + b.WriteString(fmt.Sprintf("%d:", v.pf.n)) + b.WriteString(fmt.Sprintf("%x:", v.pf.N)) + b.WriteString(fmt.Sprintf("%x:", v.pf.g)) + b.WriteString(fmt.Sprintf("%d:", int(v.h))) + b.WriteString(ih) + b.WriteByte(':') + b.WriteString(hex.EncodeToString(v.s)) + b.WriteByte(':') + b.WriteString(hex.EncodeToString(v.v)) + + return ih, b.String() +} + +// Client represents an SRP client instance +type Client struct { + s *SRP + i []byte + p []byte + a *big.Int + xA *big.Int + k *big.Int + + xK []byte + xM []byte + xAMK []byte +} + +// NewClient constructs an SRP client instance. +func (s *SRP) NewClient(I, p, salt []byte, iter int) (*Client, error) { + digest := sha256.New() // TODO: should this be s.h.New()? + if _, err := digest.Write(p); err != nil { + return nil, fmt.Errorf("srp: failed to hash password") + } + c := &Client{ + s: s, + i: s.hashbyte(I), + p: pbkdf2.Key(digest.Sum(nil), salt, iter, 32, sha256.New), + a: s.a, + k: s.hashint(s.pf.N.Bytes(), pad(s.pf.g, s.pf.n)), + } + c.xA = s.A + //fmt.Printf("Client %d:\n\tA=%x\n\tk=%x", bits, c.xA, c.k) + return c, nil +} + +// Credentials returns client public credentials to send to server +// Send <I, A> to server +func (c *Client) Credentials() string { + var b bytes.Buffer + + b.WriteString(hex.EncodeToString(c.i)) + b.WriteByte(':') + b.WriteString(hex.EncodeToString(c.xA.Bytes())) + return b.String() +} + +// Generate validates the server public credentials and generate session key +// Return the mutual authenticator. +// NB: We don't send leak any information in error messages. +func (c *Client) Generate(salt, b []byte) ([]byte, []byte, error) { + pf := c.s.pf + + B := big.NewInt(0).SetBytes(b) + if B.Cmp(big.NewInt(0)) == 0 { + return nil, nil, fmt.Errorf("srp: invalid server public key") + } + + u := c.s.hashint(pad(c.xA, pf.n), pad(B, pf.n)) + if u.Cmp(big.NewInt(0)) == 0 { + return nil, nil, fmt.Errorf("srp: invalid server public key") + } + + // S := ((B - kg^x) ^ (a + ux)) % N + + x := c.s.hashint(salt, c.s.hashbyte([]byte(":"), c.p)) + t0 := big.NewInt(0).Exp(pf.g, x, pf.N) + t0 = t0.Mul(t0, c.k) + + t1 := big.NewInt(0).Sub(B, t0) + t2 := big.NewInt(0).Add(c.a, big.NewInt(0).Mul(u, x)) + S := big.NewInt(0).Exp(t1, t2, pf.N) + + c.xK = c.s.hashbyte(S.Bytes()) + // M = H(H(N) xor H(g), H(I), s, A, B, K) + c.xM = c.s.hashbyte( + xorBytes( + c.s.hashbyte(pf.N.Bytes()), + c.s.hashbyte(pad(pf.g, pf.n)), + ), + c.i, + salt, c.xA.Bytes(), B.Bytes(), c.xK) + // M' = H(A, M, K) + c.xAMK = c.s.hashbyte(c.xA.Bytes(), c.xM, c.xK) + + return c.xM, c.xAMK, nil +} + +// ServerOk takes a 'proof' offered by the server and verifies that it is valid. +// i.e., we should compute the same hash() on M that the server did. +func (c *Client) ServerOk(proof string) bool { + h := c.s.hashbyte(c.xK, c.xM) + myh := hex.EncodeToString(h) + + return subtle.ConstantTimeCompare([]byte(myh), []byte(proof)) == 1 +} + +// RawKey returns the raw key computed as part of the protocol +func (c *Client) RawKey() []byte { + return c.xK +} + +// String represents the client parameters as a string value +func (c *Client) String() string { + pf := c.s.pf + return fmt.Sprintf("<client> g=%d, N=%x\n I=%x\n A=%x\n K=%x\n", + pf.g, pf.N, c.i, c.xA, c.xK) +} + +// hash byte stream and return as bytes +func (s *SRP) hashbyte(a ...[]byte) []byte { + h := s.h.New() + for _, z := range a { + h.Write(z) + } + return h.Sum(nil) +} + +// hash a number of byte strings and return the resulting hash as +// bigint +func (s *SRP) hashint(a ...[]byte) *big.Int { + i := big.NewInt(0) + b := s.hashbyte(a...) + i.SetBytes(b) + return i +} + +func atoi(s string) int { + i, err := strconv.Atoi(s) + if err != nil { + panic(fmt.Sprintf("srp init: can't parse int %s", s)) + } + return i +} + +func atobi(s string, base int) *big.Int { + i, ok := big.NewInt(0).SetString(s, base) + if !ok { + panic(fmt.Sprintf("srp init: can't parse bigint |%s|", s)) + } + return i +} + +func xorBytes(a, b []byte) []byte { + if len(a) != len(b) { + return nil //, fmt.Errorf("slices must be of equal length") + } + result := make([]byte, len(a)) + for i := range a { + result[i] = a[i] ^ b[i] + } + return result //, nil +} + +// pad x to n bytes if needed +func pad(x *big.Int, n int) []byte { + b := x.Bytes() + if len(b) < n { + z := n - len(b) + p := make([]byte, n) + for i := 0; i < z; i++ { + p[i] = 0 + } + + copy(p[z:], b) + b = p + } + return b +} + +// Return n bytes of random bytes. Uses cryptographically strong +// random generator +func randbytes(n int) []byte { + b := make([]byte, n) + _, err := io.ReadFull(CR.Reader, b) + if err != nil { + panic("Random source is broken!") + } + return b +} + +// Generate and return a bigInt 'bits' bits in length +func randBigInt(bits int) *big.Int { + n := bits / 8 + if (bits % 8) != 0 { + n += 1 + } + b := randbytes(n) + r := big.NewInt(0).SetBytes(b) + return r +} + +// Make a new prime field (safe prime & generator) that is 'nbits' long +// Return prime p, generator g +func NewPrimeField(nbits int) (p, g *big.Int, err error) { + var pf *primeField + + if nbits < 0 { + return nil, nil, fmt.Errorf("srp: bad field size %d", nbits) + } else if nbits == 0 { + nbits = 2048 + } + + pf, err = newPrimeField(nbits) + if err != nil { + return nil, nil, err + } + + return pf.N, pf.g, nil +} + +// Make a new prime field where the prime is 'nbits' long +// This function is not used currently. In the future, one can use this to create +// an SRP Environment where the prime field (p, g) is generated at runtime for maximum +// security. +func newPrimeField(nbits int) (*primeField, error) { + + for i := 0; i < 100; i++ { + p, err := safePrime(nbits) + if err != nil { + return nil, err + } + + for _, g0 := range simplePrimes { + g := big.NewInt(g0) + if isGenerator(g, p) { + pf := &primeField{ + g: g, + N: p, + n: nbits / 8, + } + return pf, nil + } + } + } + return nil, fmt.Errorf("srp: can't find generator after 100 tries") +} + +// Find a pre-generated safe-prime and its generator from our list below. +// In the future, we can use some other external eternal source of such things. +// NB: Generating large safe-primes is computationally taxing! It is best done offline. +func findPrimeField(bits int) (*primeField, error) { + + switch { + case bits < 0: + return nil, fmt.Errorf("srp: invalid prime-field size %d", bits) + + case bits == 0: + bits = 2048 + fallthrough + + default: + if pf, ok := pflist[bits]; ok { + return pf, nil + } + return nil, fmt.Errorf("srp: invalid prime-field size %d", bits) + } +} + +// build the database of prime fields and generators +func init() { + + one = big.NewInt(1) + pflist = make(map[int]*primeField) + lines := strings.Split(pflistStr, "\n") + for _, s := range lines { + v := strings.Split(s, ":") + b := atoi(v[0]) + + pf := &primeField{ + g: atobi(v[1], 10), + N: atobi(v[2], 0), + n: b / 8, + } + if big.NewInt(0).Cmp(pf.N) == 0 { + panic(fmt.Sprintf("srp init: N (%s) is zero", v[2])) + } + pflist[b] = pf + } +} + +// Map of bits to <g, N> tuple +const pflistStr = `1024:2:0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3 +1536:2:0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB +2048:2:0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73 +3072:5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF +4096:5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF +6144:5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF +8192:19:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF` + +// First 100 primes +var simplePrimes = []int64{ + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, + 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, + 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, + 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, + 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, +} + +type primeField struct { + g *big.Int + N *big.Int + n int // size of N in bytes +} + +// prime field list - mapped by bit size; initialized via init() above. +var pflist map[int]*primeField +var one *big.Int + +// vim: noexpandtab:sw=8:ts=8:tw=92: