CCC Spake2+ Inside

发布时间 2023-11-18 22:24:04作者: walle搬砖
 

1. 关于 SPAKE2+

     a Password Authenticated Key Exchange (PAKE) protocol run between two parties for deriving a strong shared key with no risk of disclosing the password. SPAKE2+ is an augmented PAKE protocol, as only one party makes direct use of the password during the execution of the protocol. The other party only needs a verification value at the time of the protocol execution instead of the password. The verification value can be computed once, during an offline initialization phase. The party using the password directly would typically be a client(Device), and acts as a prover, while the other party would be a server(Vehicle), and acts as verifier.
 

2. 车端侧Verifier

Spake2 使用了 Scrypt (PBKDF2-HMAC-SHA-256) 来协商密钥,其中PBKDF2 参考RFC2898,HMAC-SHA-256参照RFC6234.
 
 input: Scrypt parameters 
 output: w0, w1, L, s, pwd 
 
 begin 
 
 generate random salt s 
 generate random password pwd 
 
 z=Scrypt(pwd, s) with parameters as defined in Section 18.1.2 
 z0=left 40 bytes of z 
 z1=right 40 bytes of z 
 w0=(z0 mod (n-1)) + 1 with n being the order n of base point G as defined for NIST P-256 
 w1=(z1 mod (n-1)) + 1 with n being the order n of base point G as defined for NIST P-256 
 L=w1*G with G as defined for NIST P-256 
 
 provide pwd to Device (through web interface / SMS / Vehicle OEM app) 

 

车端需要 salt, L, w0; 设备端需要pwd

 

3. NIST P-256 曲线常数

 

 M (65 bytes)=
 04
 88 6e 2f 97 ac e4 6e 55 ba 9d d7 24 25 79 f2 99 
 3b 64 e1 6e f3 dc ab 95 af d4 97 33 3d 8f a1 2f 
 5f f3 55 16 3e 43 ce 22 4e 0b 0e 65 ff 02 ac 8e 
 5c 7b e0 94 19 c7 85 e0 ca 54 7d 55 a1 2e 2d 20

 N (65 bytes) = 
 04
 d8 bb d6 c6 39 c6 29 37 b0 4d 99 7f 38 c3 77 07 
 19 c6 29 d7 01 4d 49 a2 4b 4f 98 ba a1 29 2b 49 
 07 d6 0a a6 bf ad e4 50 08 a6 36 33 7f 51 68 c6 
 4d 9b d3 60 34 80 8c d5 64 49 0b 1e 65 6e db e7 

 

4. demo snippet

代码实现需要bouncycastle如下版本及以上

<dependency>
     <groupId>org.bouncycastle</groupId>
     <artifactId>bcpkix-jdk15on</artifactId>
     <version>1.70</version>
</dependency>

 

参照CCC协议附录 APPENDIX D OWNER PAIRING TEST VECTORS: 

public void demo() {
        String password = "pleaseletmein";
        String salt = "yellowsubmarines";
        int costN = 32768, r = 8, p = 1, dkLen=80;

        // dk = scrypt(password, salt, NCost, r, p, dkLen)
        byte[] dk = SCrypt.generate(password.getBytes(), salt.getBytes(), costN, r, p, dkLen);
        BigInteger z0 = BigIntegers.fromUnsignedByteArray(dk, 0, 40);
        BigInteger z1 = BigIntegers.fromUnsignedByteArray(dk, 40, 40);

        // w0 = (z0 mod (n − 1)) + 1
        // w1 = (z1 mod (n − 1)) + 1
        BigInteger one = BigInteger.valueOf(1);
        X9ECParameters p256CurveParams = CustomNamedCurves.getByName("secp256r1");
        BigInteger subN = p256CurveParams.getN().subtract(one);
        BigInteger w0 = z0.mod(subN).add(one);
        BigInteger w1 = z1.mod(subN).add(one);

        // L = w1 × G
        ECPoint pointL = p256CurveParams.getG().multiply(w1).normalize();

        // X = x × G + w0 × M
        BigInteger scalarx = BigIntegers.fromUnsignedByteArray(Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"));
        ECPoint pointM = new X9ECPoint(p256CurveParams.getCurve(), Hex.decodeStrict("04886e2f97ace46e55ba9dd7242579f2993b64e16ef3dcab95afd497333d8fa12f5ff355163e43ce224e0b0e65ff02ac8e5c7be09419c785e0ca547d55a12e2d20")).getPoint();
        ECPoint pointX = p256CurveParams.getG().multiply(scalarx).add(pointM.multiply(w0)).normalize();
        System.out.println(pointX.getXCoord().toString());
        System.out.println(pointX.getYCoord().toString());

        // Y = y × G + w0 × N
        BigInteger scalary = BigIntegers.fromUnsignedByteArray(Hex.decode("f1e1d1c1b1a191817161514131211101f0e0d0c0b0a090807060504030201000"));
        ECPoint pointN = new X9ECPoint(p256CurveParams.getCurve(), Hex.decodeStrict("04d8bbd6c639c62937b04d997f38c3770719c629d7014d49a24b4f98baa1292b4907d60aa6bfade45008a636337f5168c64d9bd36034808cd564490b1e656edbe7")).getPoint();
        ECPoint pointY = p256CurveParams.getG().multiply(scalary).add(pointN.multiply(w0)).normalize();
        System.out.println(pointY.getXCoord().toString());
        System.out.println(pointY.getYCoord().toString());

        /*
         *  by Vehicle
         *
         *  Z = y × (X − w0 × M)
         *  V = y × L
          */

        ECPoint pointZ = (pointX.subtract(pointM.multiply(w0))).multiply(scalary).normalize();
        System.out.println(pointZ.getXCoord().toString());
        System.out.println(pointZ.getYCoord().toString());

        ECPoint pointV = pointL.multiply(scalary).normalize();
        System.out.println(pointV.getXCoord().toString());
        System.out.println(pointV.getYCoord().toString());

        /*
         * K is the secret shared by the device and vehicle.
         * K = SHA-256(len(X) || X || len(Y) || Y || len(Z) || Z || len(V) || V || len(w0) || w0)
         * len(x) denotes the length of a string in bytes, represented as an 8-byte little-endian number.
         * 
         * HKDF-SHA-256 is used as the KDF, defined by IETF RFC 5869
         */
}

 

以上。